Skip to content

Fail fast in the simulator on surfaces usage that stalls a device - #5491

Merged
shai-almog merged 2 commits into
masterfrom
surfaces-simulator-diagnostics
Jul 30, 2026
Merged

Fail fast in the simulator on surfaces usage that stalls a device#5491
shai-almog merged 2 commits into
masterfrom
surfaces-simulator-diagnostics

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

Prompted by discussion #5490: an app that launches a Live Activity works in the simulator and freezes on an iPhone 13. The reported code funnels every surfaces call onto the EDT with callSerially, hands SurfaceImage a live com.codename1.ui.Image, and publishes a widget timeline for a kind it never registered. None of that is visible in the simulator.

Why the simulator hides it

Publishing here is a Java2D encode plus a local file write. On a device the same call writes into a shared app-group container, hands the payload to WidgetKit or ActivityKit over IPC, and -- for any image that is not already an EncodedImage -- blocks the caller on the platform UI thread while the pixels come back off the GPU (IOSNative.m createImageFile drains the render queue and dispatch_syncs onto main). Doing that on the EDT stalls the UI on hardware and costs nothing here.

What this adds

SurfaceDiagnostics, active only when Display.isSimulator() so a shipped build pays nothing, overridable via the new Surfaces.setDiagnosticsEnabled(Boolean). Conditions certain to misbehave on a device throw IllegalStateException naming the fix; the rest log once.

Throws:

  • a non-EncodedImage rasterized on the EDT -- checked inside SurfaceSerializer.encode so the stack trace lands on the app's own SurfaceImage
  • publish() targeting a kind that was never registered, listing the kinds that are registered so a typo is obvious

Warns once:

  • publish / start / update / end called on the EDT
  • one kind or activity republished past the platform's reload budget, pointing at SurfaceDynamicText and future timeline entries as the way out
  • an inert LiveActivity handle being used, since update and end are silent no-ops and that is precisely why a refused start goes unnoticed

It caught the same bug in our own reference code

SurfacesSample generated a mutable avatar and published it from EDT button handlers, with a comment conceding a real app would ship an EncodedImage. It now caches one, generating the pixels on the caller's thread and encoding inside invokeAndBlock. The developer-guide snippet's courierAvatar field changes to EncodedImage for the same reason.

Docs

Surfaces.publish claimed "no step blocks on the EDT or the platform UI thread", which was never true of the rasterizing encode -- that clause is corrected, and LiveActivity.start gains the threading section it lacked. SurfacesSnippets.java picks up the standard header because the copyright gate pulls touched files into scope.

Verification

  • SurfaceTest 28/28 (10 new), full core-unittests module 4286/4286
  • copyright-header, since-tag and ASCII gates clean over the branch range
  • real simulator runs both ways: the fixed sample publishes with only the EDT warning; a deliberately-broken copy throws with the stack pointing at its SurfaceImage

The throw-on-EDT case is unit-tested through SurfaceDiagnostics directly rather than through SurfaceSerializer, because constructing a non-EncodedImage Image needs a platform Display that suite deliberately does not have -- the end-to-end proof is the simulator run. spotbugs and javadoc were not run locally (they fail on JDK 25 for pre-existing reasons), so CI is the first check on those.

🤖 Generated with Claude Code

Surfaces publishing is cheap in the simulator and expensive on hardware:
the same call that is a Java2D encode plus a local file write here writes
into a shared app-group container, hands the payload to WidgetKit or
ActivityKit over IPC and, for any image that is not already an
EncodedImage, blocks the caller on the platform UI thread while the
pixels are read back off the GPU (IOSNative.m createImageFile does a
Metal flush and a dispatch_sync onto main). An app that publishes on the
EDT therefore looks fine in the simulator and freezes on a phone, which
is the worst possible place to find out. Discussion #5490 is exactly
this: every call funnelled onto the EDT through callSerially, an icon
handed over as a live Image, and a widget kind that was never
registered.

Add SurfaceDiagnostics, active only when Display.isSimulator() (so a
shipped build pays nothing) and overridable with the new
Surfaces.setDiagnosticsEnabled(Boolean). Conditions that are certain to
misbehave on a device throw IllegalStateException naming the fix; the
rest log once:

- throws when a non-EncodedImage is rasterized on the EDT, checked in
  SurfaceSerializer.encode so the stack lands on the app's SurfaceImage
- throws when publish() targets a kind that was never registered, and
  names the kinds that are registered so a typo is obvious
- warns once when publish/start/update/end runs on the EDT
- warns when one kind or activity is republished past the platform's
  reload budget, pointing at SurfaceDynamicText and future timeline
  entries as the way to avoid it
- warns when an inert LiveActivity handle is used, since update/end are
  silent no-ops and that is why a refused start goes unnoticed

The image check caught the same bug in our own reference code:
SurfacesSample generated a mutable avatar and published it from EDT
button handlers, with a comment conceding a real app would ship an
EncodedImage. It now caches one, generating the pixels on the caller's
thread and encoding inside invokeAndBlock; the developer-guide snippet's
courierAvatar field changes to EncodedImage for the same reason.

Surfaces.publish claimed "no step blocks on the EDT or the platform UI
thread", which was never true of the rasterizing encode; that clause is
corrected and LiveActivity.start gains the threading section it lacked.

SurfaceTest 28/28, full core-unittests module 4286/4286. Verified in a
real simulator run both ways: the fixed sample publishes with only the
EDT warning, a deliberately-broken copy throws with the stack pointing
at its SurfaceImage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 29, 2026 17:45

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ec3af8a708

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/surfaces/SurfaceDiagnostics.java Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds simulator-only “fail fast” diagnostics to the Surfaces API to catch usage patterns that appear fine in the JavaSE simulator but can stall or silently no-op on real devices (notably iOS), and updates samples/docs/tests accordingly.

Changes:

  • Introduces SurfaceDiagnostics and wires it into Surfaces.publish(), LiveActivity.start/update/end(), and SurfaceSerializer.encode() to throw on known-bad patterns and warn once on likely-bad patterns (simulator-only unless explicitly overridden).
  • Adds unit tests covering diagnostic enablement/inertness and key failure/warn behaviors.
  • Updates the Surfaces sample and developer-guide snippets to use cached EncodedImage payloads to avoid rasterizing images during publish.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
Samples/samples/SurfacesSample/SurfacesSample.java Caches the courier avatar as an EncodedImage to avoid publish-time rasterization on EDT.
maven/core-unittests/src/test/java/com/codename1/surfaces/SurfaceTest.java Adds tests for simulator diagnostics behavior (throw/warn/inert).
docs/demos/common/src/main/java/com/codenameone/developerguide/surfaces/SurfacesSnippets.java Updates snippet to use EncodedImage and adds standard header.
CodenameOne/src/com/codename1/surfaces/SurfaceSerializer.java Calls diagnostics hook before rasterizing non-EncodedImage payloads.
CodenameOne/src/com/codename1/surfaces/Surfaces.java Adds diagnostics enable/disable API and invokes diagnostics during publish/reset.
CodenameOne/src/com/codename1/surfaces/SurfaceDiagnostics.java New simulator-only diagnostics implementation (throws + one-time warnings).
CodenameOne/src/com/codename1/surfaces/LiveActivity.java Adds threading docs and diagnostics hooks for EDT usage, rate limiting, and inert handles.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread CodenameOne/src/com/codename1/surfaces/Surfaces.java
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [HTML preview] [Download]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 1 findings (Normal: 1)
      • Top findings
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 151 screenshots: 151 matched.

Native Android coverage

  • 📊 Line coverage: 7.85% (7601/96841 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 7.71% (39571/513127), branch 2.79% (1359/48627), complexity 3.15% (1642/52177), method 4.87% (1343/27595), class 9.95% (366/3679)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 7.85% (7601/96841 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 7.71% (39571/513127), branch 2.79% (1359/48627), complexity 3.15% (1642/52177), method 4.87% (1343/27595), class 9.95% (366/3679)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend scalar fallback (no native SIMD)
SIMD int-add (64K x300) java 324ms / native 169ms = 1.9x speedup
SIMD float-mul (64K x300) java 126ms / native 72ms = 1.7x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 78.000 ms
Base64 CN1 decode 72.000 ms
Base64 native encode 427.000 ms
Base64 encode ratio (CN1/native) 0.183x (81.7% faster)
Base64 native decode 343.000 ms
Base64 decode ratio (CN1/native) 0.210x (79.0% faster)
Image encode benchmark status skipped (SIMD unsupported)

Two findings from the PR review bots, both correct.

registeredKinds was a plain ArrayList that registerWidgetKind() mutated
while the new isKindRegistered() (and registerWidgetKind itself) walked
it, so a registration racing a publish could throw
ConcurrentModificationException. The API is explicitly callable from any
thread, so guard every touch of the list with its own monitor and let
readers copy out rather than iterate live. This also closes the same
hazard that already existed in registerWidgetKind before this branch.

The EDT image diagnostic suggested EncodedImage.createFromImage(img,
false) as the remedy without saying where to run it. Following that
advice at the same call site performs the identical ImageIO.save and
pays the very stall the check exists to prevent -- and then hides it,
because the serializer afterwards sees an EncodedImage. The message now
says to convert ONCE, off the EDT, and cache the result, which is what
the updated sample does.

Adds a regression test that pads the registry and looks the target up
last so the lookup genuinely overlaps the writer. Being a race it
reproduces the unsynchronized failure about one run in three; it never
false-fails with the locking in place (5 consecutive clean runs).
SurfaceTest 29/29.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 29, 2026 18:16

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: daf7051afa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +143 to +144
if (!enabled() || Surfaces.isKindRegistered(kindId)) {
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Register the analog-clock guide kind before enforcing this check

When a reader runs the repository's SurfacesSnippets examples, registerKinds() registers only delivery_status, while publishClockTimeline() publishes analog_clock; because diagnostics are enabled by default in the simulator, this new check throws before the documented clock timeline can be published. Add analog_clock to the guide's runtime registrations and matching manifest example so the documented flow remains runnable.

Useful? React with 👍 / 👎.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (3)

CodenameOne/src/com/codename1/surfaces/SurfaceDiagnostics.java:62

  • override and edtForTests are written in setEnabled()/setEdtForTests() and read in enabled()/isEdt() without any synchronization. Since surfaces APIs (and these diagnostics) are callable from any thread, this is a real Java memory-visibility race (other threads may see stale values). Mark these fields volatile (or use AtomicReference<Boolean>) to make toggling diagnostics/test seams reliably visible across threads.
    /// null = follow the platform (simulator on, everything else off), non-null = forced.
    private static Boolean override;

    /// null = ask the real Display, non-null = forced (tests only, see [#setEdtForTests(Boolean)]).
    private static Boolean edtForTests;

CodenameOne/src/com/codename1/surfaces/Surfaces.java:111

  • Removing from registeredKinds inside an enhanced for-loop is brittle (it relies on breaking immediately to avoid iterator state checks). Using an explicit Iterator and Iterator.remove() avoids any risk of ConcurrentModificationException if this loop is refactored later, and makes the intent clearer.
            for (WidgetKind k : registeredKinds) {
                if (k.getId().equals(kind.getId())) {
                    registeredKinds.remove(k);
                    break;
                }

CodenameOne/src/com/codename1/surfaces/Surfaces.java:193

  • publish() doesn’t validate kindId or timeline. If timeline is null, SurfaceSerializer.serializeTimeline() will throw a NullPointerException (dereferencing timeline) rather than a clear argument error. Adding an explicit guard improves API behavior and makes failures easier to diagnose (especially now that diagnostics run before serialization).
    public static void publish(String kindId, WidgetTimeline timeline) {
        SurfaceDiagnostics.requireRegisteredKind(kindId);
        SurfaceDiagnostics.offEdtPreferred("Surfaces.publish");
        SurfaceDiagnostics.noteRepublish("kind:" + kindId, "widget kind \"" + kindId + "\"");

@shai-almog

shai-almog commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 181 screenshots: 181 matched.
✅ JavaScript-port screenshot tests passed.

@shai-almog

shai-almog commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 148 screenshots: 148 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 274 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 59ms / native 3ms = 19.6x speedup
SIMD float-mul (64K x300) java 64ms / native 3ms = 21.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 182.000 ms
Base64 CN1 decode 114.000 ms
Base64 native encode 645.000 ms
Base64 encode ratio (CN1/native) 0.282x (71.8% faster)
Base64 native decode 494.000 ms
Base64 decode ratio (CN1/native) 0.231x (76.9% faster)
Base64 SIMD encode 53.000 ms
Base64 encode ratio (SIMD/CN1) 0.291x (70.9% faster)
Base64 SIMD decode 53.000 ms
Base64 decode ratio (SIMD/CN1) 0.465x (53.5% faster)
Base64 encode ratio (SIMD/native) 0.082x (91.8% faster)
Base64 decode ratio (SIMD/native) 0.107x (89.3% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 8.000 ms
Image createMask (SIMD on) 6.000 ms
Image createMask ratio (SIMD on/off) 0.750x (25.0% faster)
Image applyMask (SIMD off) 76.000 ms
Image applyMask (SIMD on) 39.000 ms
Image applyMask ratio (SIMD on/off) 0.513x (48.7% faster)
Image modifyAlpha (SIMD off) 40.000 ms
Image modifyAlpha (SIMD on) 44.000 ms
Image modifyAlpha ratio (SIMD on/off) 1.100x (10.0% slower)
Image modifyAlpha removeColor (SIMD off) 57.000 ms
Image modifyAlpha removeColor (SIMD on) 54.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.947x (5.3% faster)

@shai-almog

shai-almog commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 144 screenshots: 144 matched.
✅ Native Apple TV (tvOS, Metal) screenshot tests passed.

@shai-almog

shai-almog commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 513 seconds

Build and Run Timing

Metric Duration
Simulator Boot 88000 ms
Simulator Boot (Run) 1000 ms
App Install 16000 ms
App Launch 6000 ms
Test Execution 506000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 162ms / native 4ms = 40.5x speedup
SIMD float-mul (64K x300) java 183ms / native 11ms = 16.6x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 365.000 ms
Base64 CN1 decode 93.000 ms
Base64 native encode 674.000 ms
Base64 encode ratio (CN1/native) 0.542x (45.8% faster)
Base64 native decode 312.000 ms
Base64 decode ratio (CN1/native) 0.298x (70.2% faster)
Base64 SIMD encode 49.000 ms
Base64 encode ratio (SIMD/CN1) 0.134x (86.6% faster)
Base64 SIMD decode 79.000 ms
Base64 decode ratio (SIMD/CN1) 0.849x (15.1% faster)
Base64 encode ratio (SIMD/native) 0.073x (92.7% faster)
Base64 decode ratio (SIMD/native) 0.253x (74.7% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 7.000 ms
Image createMask (SIMD on) 2.000 ms
Image createMask ratio (SIMD on/off) 0.286x (71.4% faster)
Image applyMask (SIMD off) 54.000 ms
Image applyMask (SIMD on) 49.000 ms
Image applyMask ratio (SIMD on/off) 0.907x (9.3% faster)
Image modifyAlpha (SIMD off) 38.000 ms
Image modifyAlpha (SIMD on) 100.000 ms
Image modifyAlpha ratio (SIMD on/off) 2.632x (163.2% slower)
Image modifyAlpha removeColor (SIMD off) 442.000 ms
Image modifyAlpha removeColor (SIMD on) 106.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.240x (76.0% faster)

@shai-almog

shai-almog commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 143 screenshots: 143 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 463 seconds

Build and Run Timing

Metric Duration
Simulator Boot 81000 ms
Simulator Boot (Run) 0 ms
App Install 15000 ms
App Launch 1000 ms
Test Execution 495000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 139ms / native 2ms = 69.5x speedup
SIMD float-mul (64K x300) java 267ms / native 2ms = 133.5x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 298.000 ms
Base64 CN1 decode 214.000 ms
Base64 native encode 589.000 ms
Base64 encode ratio (CN1/native) 0.506x (49.4% faster)
Base64 native decode 390.000 ms
Base64 decode ratio (CN1/native) 0.549x (45.1% faster)
Base64 SIMD encode 109.000 ms
Base64 encode ratio (SIMD/CN1) 0.366x (63.4% faster)
Base64 SIMD decode 81.000 ms
Base64 decode ratio (SIMD/CN1) 0.379x (62.1% faster)
Base64 encode ratio (SIMD/native) 0.185x (81.5% faster)
Base64 decode ratio (SIMD/native) 0.208x (79.2% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 15.000 ms
Image createMask (SIMD on) 7.000 ms
Image createMask ratio (SIMD on/off) 0.467x (53.3% faster)
Image applyMask (SIMD off) 49.000 ms
Image applyMask (SIMD on) 38.000 ms
Image applyMask ratio (SIMD on/off) 0.776x (22.4% faster)
Image modifyAlpha (SIMD off) 493.000 ms
Image modifyAlpha (SIMD on) 134.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.272x (72.8% faster)
Image modifyAlpha removeColor (SIMD off) 123.000 ms
Image modifyAlpha removeColor (SIMD on) 176.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.431x (43.1% slower)

@shai-almog

shai-almog commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 217 screenshots: 217 matched.
✅ Native Apple Watch (watchOS, Core Graphics) screenshot tests passed.

@shai-almog
shai-almog merged commit f89cee8 into master Jul 30, 2026
32 of 33 checks passed
@shai-almog
shai-almog deleted the surfaces-simulator-diagnostics branch July 30, 2026 01:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants